/*
 * This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform".
 *
 * (c) Matthias L. Jugel, Marcus Meißner 1996-2005. All Rights Reserved.
 *
 * Please visit http://javatelnet.org/ for updates and contact.
 *
 * --LICENSE NOTICE--
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * --LICENSE NOTICE--
 *
 */

package de.mud.jta.plugin;

import de.mud.jta.Plugin;
import de.mud.jta.FilterPlugin;
import de.mud.jta.PluginBus;
import de.mud.jta.PluginConfig;
import de.mud.jta.event.ConfigurationListener;
import de.mud.jta.event.SocketListener;
import de.mud.jta.event.SocketRequest;

import java.io.IOException;

/**
 * The timeout plugin looks at the incoming and outgoing data stream and tries
 * to close the connection gracefully if the timeout occured or if not graceful
 * exit command was configured simply closed the connection.
 * <P>
 * <B>Maintainer:</B> Matthias L. Jugel
 * 
 * @version $Id: Timeout.java 499 2005-09-29 08:24:54Z leo $
 * @author Matthias L. Jugel, Marcus Mei�ner
 */
public class Timeout extends Plugin implements FilterPlugin, SocketListener,
	Runnable
{

    private final static int debug = 0;

    protected int timeout = 0;
    protected String timeoutCommand = null;
    protected String timeoutWarning = null;
    protected Thread timeoutThread = null;

    private PluginBus pluginBus;

    /**
     * Create the new timeout plugin.
     */
    public Timeout(final PluginBus bus, final String id)
    {
	super(bus, id);

	// register socket listener
	bus.registerPluginListener(this);

	bus.registerPluginListener(new ConfigurationListener()
	{
	    public void setConfiguration(PluginConfig config)
	    {
		String tos = config.getProperty("Timeout", id, "seconds");
		if (tos != null)
		{
		    try
		    {
			timeout = Integer.parseInt(tos);
		    }
		    catch (Exception e)
		    {
			Timeout.this.error("timeout (" + timeout + ") "
				+ "is not an integer, timeout disabled");
		    }
		    timeoutCommand = config.getProperty("Timeout", id,
			    "command");
		    timeoutWarning = config.getProperty("Timeout", id,
			    "warning");
		}
	    }
	});
	pluginBus = bus;
    }

    /**
     * Sleep for the timeout beeing. The thread gets interrupted if data is
     * transmitted and will shutdown the connection as soon as the timeout wakes
     * up normally.
     */
    public void run()
    {
	boolean ok = false;

	// loop around until the thread is kicked down the stream ...
	while (timeoutThread != null)
	{
	    try
	    {
		ok = false;
		timeoutThread.sleep(1000 * timeout);
	    }
	    catch (InterruptedException e)
	    {
		ok = true;
	    }

	    // if the timeout finished sucessfully close the connection
	    if (!ok)
	    {
		error("data connection timeout, shutting down");

		// first try it gracefully by sending the configured exit
		// command
		if (timeoutCommand != null)
		{
		    error("sending graceful exit command ...");
		    try
		    {
			write(timeoutCommand.getBytes());
		    }
		    catch (IOException e)
		    {
			error("could not send exit command");
		    }
		    timeoutThread = null;
		    final Thread grace = new Thread(new Runnable()
		    {
			public void run()
			{
			    try
			    {
				Thread.currentThread().sleep(1000 * timeout);
				Timeout.this.pluginBus
					.broadcast(new SocketRequest());
			    }
			    catch (InterruptedException e)
			    {
				// ignore exception
			    }
			}
		    });
		    grace.start();
		}
		else
		    // if not graceful exit exists, be rude
		    bus.broadcast(new SocketRequest());
	    }
	}
    }

    /** Start the timeout countdown. */
    public void connect(String host, int port) throws IOException
    {
	if (timeout > 0)
	{
	    timeoutThread = new Thread(Timeout.this);
	    timeoutThread.start();
	}
    }

    /** Stop the timeout */
    public void disconnect() throws IOException
    {
	if (timeoutThread != null)
	{
	    Thread tmp = timeoutThread;
	    timeoutThread = null;
	    tmp.interrupt();
	}
    }

    FilterPlugin source;

    public void setFilterSource(FilterPlugin plugin)
    {
	source = plugin;
    }

    public FilterPlugin getFilterSource()
    {
	return source;
    }

    public int read(byte[] b) throws IOException
    {
	int n = source.read(b);
	if (n > 0 && timeoutThread != null)
	    timeoutThread.interrupt();
	return n;
    }

    public void write(byte[] b) throws IOException
    {
	source.write(b);
	if (timeoutThread != null)
	    timeoutThread.interrupt();
    }
}
